﻿using Red.CommUtils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Red.EngineData.Tests
{
    /// <summary>
    /// Fake channel to simulate communication, but inside the same application.
    /// </summary>
    public class FakeChannel : IChannel, IResponse
    {
        private event ChannelIncomingMessageEventHandler OnResponseEvent;
        private FakeChannel _connectedTo = null;

        public string Name
        {
            get { return "FakeChannel"; }
        }

        /// <summary>
        /// Fake response interface implementation, used by SendAck method
        /// </summary>
        private class FakeResponseGateway : IResponse
        {
            private MessageResponseHandler _handler;

            public FakeResponseGateway(MessageResponseHandler handler)
            {
                _handler = handler;
            }

            public void Send(comm.Message message)
            {
                _handler(message);
            }
        }

        /// <summary>
        /// A way to connect channels with each other.
        /// </summary>
        /// <param name="connectedTo">Channel to connect to.</param>
        private void ConnectTo(FakeChannel connectedTo)
        {
            _connectedTo = connectedTo;
        }

        /// <summary>
        /// Handle incoming message, called directly on fake channel by another fake channel. This is used by Send method.
        /// </summary>
        /// <param name="message">Incoming message.</param>
        /// <param name="response">A way to send response.</param>
        private void OnIncomingMessage(comm.Message message, IResponse response)
        {
            OnResponseEvent(message, response);
        }

        /// <summary>
        /// Indicates if the channel is ready to be used.
        /// </summary>
        /// <returns>True if true, false if false.</returns>
        bool IChannel.IsOpen()
        {
            return _connectedTo != null;
        }

        // Called by user
        void IChannel.Send(comm.Message message)
        {
            _connectedTo.OnIncomingMessage(message, this);
        }

        // Called by user
        void IChannel.SendAck(comm.Message message, MessageResponseHandler handler)
        {
            _connectedTo.OnIncomingMessage(message, new FakeResponseGateway(handler) );
        }

        // Called by user
        /// <summary>
        /// We don't want any system to call SendAndWait. It's only meant to be used in special cases like engine launching.
        /// </summary>
        /// <param name="message">Message to send.</param>
        /// <param name="timeoutInMS">Time to wait.</param>
        /// <returns></returns>
        comm.Message IChannel.SendAndWait(comm.Message message, int timeoutInMS)
        {
            throw new Exception("Test: do not use SendAndWait inside engine data project.");
        }

        // Called by user
        void IChannel.RegisterListener(ChannelIncomingMessageEventHandler listener)
        {
            OnResponseEvent += listener;
        }

        // Called by user
        void IChannel.UnregisterListener(ChannelIncomingMessageEventHandler listener)
        {
            OnResponseEvent -= listener;
        }

        // Called by service
        void IResponse.Send(comm.Message message)
        {
            OnResponseEvent(message, _connectedTo);
        }

        /// <summary>
        /// Use this to create channels which are already connected to each other.
        /// </summary>
        /// <param name="user">User side - i.e. C# service user</param>
        /// <param name="service">Service side = i.e. C++ service</param>
        public static void CreateConnection(out IChannel user, out IChannel service)
        {
            FakeChannel userChannel = new FakeChannel();
            FakeChannel serviceChannel = new FakeChannel();

            userChannel.ConnectTo(serviceChannel);
            serviceChannel.ConnectTo(userChannel);

            user = userChannel;
            service = serviceChannel;
        }

        public Task<bool> TryReconnect( int timeout )
        {
            throw new NotImplementedException();
        }

        public bool IsOpen()
        {
            throw new NotImplementedException();
        }

        public void ShutDown()
        {
            throw new NotImplementedException();
        }

        public void SendAck( comm.Message message, MessageResponseHandler handler )
        {
            throw new NotImplementedException();
        }

        public comm.Message SendAndWait( comm.Message message, int timeoutInMS )
        {
            throw new NotImplementedException();
        }

        public void RegisterListener( ChannelIncomingMessageEventHandler listener )
        {
            throw new NotImplementedException();
        }

        public void UnregisterListener( ChannelIncomingMessageEventHandler listener )
        {
            throw new NotImplementedException();
        }
        public ulong TotalBytesReceived
        {
            get { throw new NotImplementedException(); }
        }

        public ulong TotalBytesSent
        {
            get { throw new NotImplementedException(); }
        }
    }
}
